
from logging import DEBUG
import serial.tools.list_ports
import serial
import struct
from enum import Enum, auto
from typing import Optional

from log import CP8212_VOLTAGE_IDENT, add_log

class ComPortCmd(Enum):
    Voltage = auto()
    Capacity = auto()
    CapacityVoltage = auto()
    Temperature = auto()
    Current = auto()
class ComPortMethod(Enum):
    Get = auto()
    Set = auto()
class ComPortCalculate(Enum):
    Crc16 = auto()
    ModbusCrc16 = auto()
class SerialOption:
    port=str
    baudrate=int
    bytesize=int
    parity=str
    stopbits=float
    timeout=float
    def __init__(self,port:str,baudrate:int,bytesize:int,parity:str,stopbits:float,timeout:float):
        self.port = port
        self.baudrate = baudrate
        self.bytesize = bytesize
        self.parity = parity
        self.stopbits = stopbits
        self.timeout = timeout


""" 串口基础模块 """
class ComPortModel:
    receive_size = int
    byteorder = str
    cmd = ComPortCmd
    method = ComPortMethod
    calculate = ComPortCalculate
    options = SerialOption
    value_range = (int,int)
    divided = float
    msg = str
    unit = str
    def __init__(self, options: SerialOption, receive_size: int, byteorder: str, cmd: ComPortCmd, method: ComPortMethod, calculate:ComPortCalculate):
        self.receive_size = receive_size
        self.byteorder = byteorder
        self.cmd = cmd
        self.method = method
        self.calculate = calculate
        self.options = options
        self.value_range = (0,0)
        self.msg = ""
        self.divided = 1.0
        self.unit = ""
    """ 设置方法 """
    def set_method(self, method: ComPortMethod):
        self.method = method
    """ 设置命令 """
    def set_cmd(self, cmd: ComPortCmd):
        self.cmd = cmd
    """ 同步命令 """
    def sync_msg(self)->None:
        return None
    """ 解析算法 """
    def parse_calculate(self,data:bytes) -> Optional[bytes]:
        calculate = None
        if self.calculate == ComPortCalculate.Crc16:
            # Calculate CRC
            calculate = calculate_crc16(data)
        elif self.calculate == ComPortCalculate.ModbusCrc16:
            calculate = modbus_crc16(data)
        return calculate
    """ 获取串口列表 """
    def get_port(self,flag:str):
        try:
            # 获取可用串口列表
            for port in serial.tools.list_ports.comports():
                for p in port:
                    if flag in p:
                        res = p.split(" ")[-1].strip("(").strip(")")
                        if res:
                            return res
        except:pass
        return ""
        
class ComPortResult:
    address= str
    command= str
    byteCount=int
    value=float
    calculate=str
    def __init__(self, address: str, command: str, byteCount: int, value: float, calculate: str):
        self.address = address
        self.command = command
        self.byteCount = byteCount
        self.value = value
        self.calculate = calculate
        
""" 串口模块实体 """
class ComPort:
    def __init__(self, model: ComPortModel):
        self.model = model
        self.ser = None
    """ 设置模型 """
    def set_model(self, model:ComPortModel):
        self.model = model
    """ 串口连接 """
    def connect(self):
        o = self.model.options
        self.ser = serial.Serial(port=o.port,baudrate=o.baudrate,bytesize=o.bytesize,parity=o.parity,stopbits=o.stopbits,timeout=o.timeout)
    """ 结束连接 """
    def disconnect(self):
        self.ser.close()
    """ 发送命令 """
    def send(self) -> Optional[int]:
        try:
            self.model.sync_msg()
            msg = self.model.msg.replace(" ","")
            # Convert command string to bytes
            msg_bytes = bytes.fromhex(msg)
            calculate = self.model.parse_calculate(msg_bytes)
            if calculate == None:
                return None
            full_msg = msg_bytes + calculate
            # Send command
            add_log(f"发送: {bytes_to_display(full_msg)}",DEBUG,CP8212_VOLTAGE_IDENT)
            res = self.ser.write(full_msg)
            return res
        except:
            return None
    """ 接受消息 """
    def receive(self) -> Optional[str]:
        try:
            # Read response
            res:bytes = self.ser.read(self.model.receive_size)
            add_log(f"接收: {bytes_to_display(res)}",DEBUG,CP8212_VOLTAGE_IDENT)
            hex_bytes = res.hex()
            return hex_bytes
        except:
            return None
    """ 解析消息 """
    def parse(self, data:str) -> Optional[ComPortResult]:
        try:
            # 将十六进制字符串转换为字节
            response_bytes = bytes.fromhex(data)
            # 解析数据
            address = response_bytes[0]
            command = response_bytes[1]
            byte_count = response_bytes[2]
            # 解析数值
            _value = self.parse_value(response_bytes) 
            value = float(_value) / self.model.divided
            # CRC校验解析
            crc_low = response_bytes[-2]
            crc_high = response_bytes[-1]
            crc = (crc_high << 8) | crc_low
            return  ComPortResult(address,command,byte_count,value,crc)
        except:
            return None
    
    """ 解析结果范围 """
    def parse_value(self,response_bytes:bytes):
        start = self.model.value_range[0]
        end = self.model.value_range[1]
        rbytes = response_bytes[start:end]
        raw_value = int.from_bytes(rbytes, byteorder=self.model.byteorder)
        res = parse_float(raw_value)
        return res 
    
"""解析CP8211浮点数值"""
def parse_float(raw_value:int) -> float:
    n = raw_value // 10000
    mantissa = raw_value % 10000
    res = mantissa / (10 ** n) * 100
    return res

""" CRC校验码采用CRC16格式，校验码排列顺序是低位在前,高位在后 """
def calculate_crc16(data:bytes):
    crc = 0xFFFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x0001:
                crc >>= 1
                crc ^= 0xA001
            else:
                crc >>= 1
    return struct.pack('<H', crc)

"""
Modbus CRC16校验码生成算法
:param data_hex: 十六进制字符串（如"01 04 00 02 00 01"）
:return: 带CRC校验码的完整报文（如"01 04 00 02 00 01 90 0A"）
"""
def modbus_crc16(data:bytes) -> bytes:
    # CRC计算核心算法
    crc = 0xFFFF  # 初始化寄存器[1,3](@ref)
    for byte in data:
        crc ^= byte  # 逐字节异或[5](@ref)
        for _ in range(8):  # 8次位运算
            if crc & 0x0001:  # 检测最低位
                crc = (crc >> 1) ^ 0xA001  # 多项式异或[3,6](@ref)
            else:
                crc >>= 1  # 简单右移
    # 转换为小端序字节流并拼接
    crc_bytes = crc.to_bytes(2, byteorder='little')  # 低字节在前[5,7](@ref)
    return crc_bytes 

def bytes_to_display(bytes_data: bytes):
    # 1. 转换为连续十六进制字符串
    hex_str = bytes_data.hex().upper()  # 转换为大写字母[5,7](@ref)
    # 2. 每两个字符插入空格
    formatted_str = ' '.join([hex_str[i:i+2] for i in range(0, len(hex_str), 2)])
    return formatted_str